#include <jni.h>
#include <string>
#include "SDL.h"
#include <android/log.h>
#include "global.h"
#include "sdlmgr.h"
#include "decoder.h"


extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
#include "libavfilter/avfilter.h"
#include "libavutil/imgutils.h"
#include "libavcodec/jni.h"
}

//使用NDK的log
//#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"ERROR: ", __VA_ARGS__)
//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"INFO: ", __VA_ARGS__)

//C字符串转java字符串
jstring str2Jstring(JNIEnv* env, const char* pStr)
{
    int        strLen    = strlen(pStr);
    LOGI("Have get strLen: %d", strLen);
    jclass     jstrObj   = env->FindClass("java/lang/String");
    jmethodID  methodId  = env->GetMethodID(jstrObj, "<init>", "([BLjava/lang/String;)V");
    jbyteArray byteArray = env->NewByteArray(strLen);
    jstring    encode    = env->NewStringUTF("utf-8");
    env->SetByteArrayRegion(byteArray, 0, strLen, (jbyte*)pStr);

    return (jstring)env->NewObject(jstrObj, methodId, byteArray, encode);
}

//java字符串转C字符串
char* Jstring2str(JNIEnv* env, jstring jstr)
{
    char* pStr = NULL;
    jclass     jstrObj   = env->FindClass("java/lang/String");
    jstring    encode    = env->NewStringUTF("utf-8");
    jmethodID  methodId  = env->GetMethodID(jstrObj, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(jstr, methodId, encode);
    jsize      strLen    = env->GetArrayLength(byteArray);
    jbyte      *jBuf     = env->GetByteArrayElements(byteArray, JNI_FALSE);
    if (strLen > 0)
    {
        pStr = (char*)malloc(strLen + 1);
        if (!pStr)
        {
            return NULL;
        }
        memcpy(pStr, jBuf, strLen);
        pStr[strLen] = 0;
    }
    env->ReleaseByteArrayElements(byteArray, jBuf, 0);
    return pStr;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_org_libsdl_app_SDLActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jstring JNICALL
Java_org_libsdl_app_SDLActivity_InitFFMPEG(
        JNIEnv* env,
        jobject /* this */) {
    //1.注册所有组件
    av_register_all();

    std::string hello = "av_register_all OK";
    return env->NewStringUTF(hello.c_str());
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    LOGI("ffmpeg JNI_OnLoad");

    av_jni_set_java_vm(vm, reserved);
    return JNI_VERSION_1_6;
}

extern "C"
JNIEXPORT int JNICALL
Java_org_libsdl_app_SDLActivity_Open(JNIEnv* env, jclass cls, jstring strPath)
{
    const char *mediaUri = env->GetStringUTFChars(strPath, 0);
    LOGI("Open path=%s\n",mediaUri);

//SDL Parameters
    CSDLMgr        sdlMgr;
    CMyDecoder     myDecoder;
    unsigned char *pAudioBuffer = new unsigned char[MAX_AUDIO_FRAME_SIZE];
    int nAudioBufLen = 0;

    LOGI("Open mediaUri=%s\n",mediaUri);

    AVFrame       *pFrame_out = av_frame_alloc();
    AVFrame       *pFrame_out_a = av_frame_alloc();
    if(myDecoder.OpenFile(mediaUri)<0)
    {
        return -1;
    }
    myDecoder.InitSws(pFrame_out);

    SMediaInfo srcMediaInfo;
    myDecoder.GetMediaInfo(srcMediaInfo);
    LOGI("FPS=%d",srcMediaInfo.tagVInfo.nFPS);

    SMediaInfo encoderInfo(srcMediaInfo);
    //encoderInfo.tagAInfo.nResample = 8000;

    myDecoder.SetFilterInfo(encoderInfo);
    myDecoder.SetOutSwrInfo(encoderInfo);

    int nW,nH;
    myDecoder.GetFrameWH(nW,nH);
    LOGI("GetFrameWH  W=%d,H=%d\n",nW,nH);

    if(sdlMgr.Init(nW,nH,encoderInfo.tagVInfo.nFPS,encoderInfo.nTimeLen)<0)
    {
        LOGE("SDL init error");
    }

    if(encoderInfo.bEnableAudio==true)
    {
        sdlMgr.InitAudio(encoderInfo.tagAInfo.nResample,
                         encoderInfo.tagAInfo.nChannels,
                         encoderInfo.tagAInfo.nFrameSize);
    }

    // Read frames
    while (!sdlMgr.NeedExit()) {

        if(!sdlMgr.NeedPlayCtrl())
        {
            SDL_Delay(10);
            continue;
        }

        enMediaType  enMT = enMT_NONE;
        double pts=0;
        int nRet = myDecoder.GetFrameData(pFrame_out,pFrame_out_a,enMT,pts);
        if (nRet>0)
        {
            switch(enMT)
            {
                case enMT_VIDEO:
                {
                    sdlMgr.InputVideoData(pFrame_out->data[0], pFrame_out->linesize[0],
                                   pFrame_out->data[1],pFrame_out->linesize[1],
                                   pFrame_out->data[2],pFrame_out->linesize[2],pts);
                    //SDL_Delay(1000/srcMediaInfo.tagVInfo.nFPS-15);
                }
                    break;
                case enMT_AUDIO:
                {
                    if(encoderInfo.bEnableAudio==false)
                        break;

                    myDecoder.GetAudioData(pFrame_out_a,pAudioBuffer,nAudioBufLen);
                    sdlMgr.InputAudioData(pAudioBuffer,nAudioBufLen,pts);
                }
                    break;
            }


            //Delay 40ms，25FPS
//            if(enMT==enMT_VIDEO)
//            SDL_Delay(20);
        } else if(nRet < 0)
        {
            //播放完
            LOGI("GetFrameData  error!");
            break;
        }
    }

    if(pAudioBuffer!=NULL)
    {
        delete[] pAudioBuffer;
        pAudioBuffer = NULL;
    }

    if(pFrame_out!=NULL){
        av_frame_free(&pFrame_out);
        pFrame_out = NULL;
    }

    if(pFrame_out_a!=NULL){
        av_frame_free(&pFrame_out_a);
        pFrame_out_a = NULL;
    }

    SDL_Quit();
    return 0;
}

struct SBMPInfo
{
    SDL_Texture* texture;
    Uint16 w;
    Uint16 h;
} ;

/* Adapted from SDL's testspriteminimal.c */
SBMPInfo LoadBMP(const char* file, SDL_Renderer* renderer)
{
    SBMPInfo result;
    result.texture = NULL;
    result.w = 0;
    result.h = 0;

    SDL_Surface* temp;

    /* Load the sprite image */
    temp = SDL_LoadBMP(file);
    if (temp == NULL)
    {
        fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
        return result;
    }
    result.w = temp->w;
    result.h = temp->h;

    /* Create texture from the image */
    result.texture = SDL_CreateTextureFromSurface(renderer, temp);
    if (!result.texture) {
        fprintf(stderr, "Couldn't create texture: %s\n", SDL_GetError());
        SDL_FreeSurface(temp);
        return result;
    }
    SDL_FreeSurface(temp);

    return result;
}

void draw(SDL_Window* window, SDL_Renderer* renderer, const SBMPInfo tagBMP)
{
    int w, h;
    SDL_GetWindowSize(window, &w, &h);
    SDL_Rect destRect = {w/2 - tagBMP.w/2, h/2 - tagBMP.h/2, tagBMP.w, tagBMP.h};
    /* Blit the sprite onto the screen */
    SDL_RenderCopy(renderer, tagBMP.texture, NULL, &destRect);
}

int main(int argc, char *argv[])
{
#if 0//show bmp
//    SDL_Window *window;
//    SDL_Renderer *renderer;

//    if(SDL_CreateWindowAndRenderer(0, 0, 0, &window, &renderer) < 0)
//        exit(2);
//
//    SBMPInfo tagBMP = LoadBMP("logo.bmp", renderer);
//    if(tagBMP.texture == NULL)
//        exit(2);

    LOGI("into main\n");

    /* Main render loop */
    Uint8 done = 0;
    SDL_Event event;
    while(!done)
    {
        /* Check for events */
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT || event.type == SDL_KEYDOWN || event.type == SDL_FINGERDOWN)
            {
                done = 1;
            }
        }


        /* Draw a gray background */
//        SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
//        SDL_RenderClear(renderer);
//
//        draw(window, renderer, tagBMP);
//
//        /* Update the screen! */
//        SDL_RenderPresent(renderer);

        SDL_Delay(10);
    }
    LOGI("exit from main\n");
    //exit(0);
#endif  //show bmp
return 0;

}